home *** CD-ROM | disk | FTP | other *** search
/ Assassins - Ultimate CD Games Collection 4 / Assassins 4 (1999)(Weird Science).iso / mui / mui_developer / c / examples / subtask.c < prev    next >
C/C++ Source or Header  |  1997-03-10  |  21KB  |  811 lines

  1. /*
  2.  
  3. This little demo show how to write classes which need a long
  4. time to render their contents. In this case, we take a little
  5. fractal algorithm as example. The actual calculations are
  6. done in a separate task, the display is updated from time
  7. to time.
  8.  
  9. */
  10.  
  11. #include "demo.h"
  12. #include <dos/dostags.h>
  13. #include <exec/memory.h>
  14.  
  15.  
  16. /* Pixel dimensions of our fractal */
  17.  
  18. #define FRACTALWIDTH  300
  19. #define FRACTALHEIGHT 300
  20.  
  21.  
  22. /* Fractal Description */
  23.  
  24. struct FractalDesc
  25. {
  26.     float left;
  27.     float right;
  28.     float top;
  29.     float bottom;
  30. };
  31.  
  32. #define MaxIterations 60
  33.  
  34. /* Attributes and methods for the custom class */
  35.  
  36. #define MUISERIALNR_STUNTZI 1
  37. #define TAGBASE_STUNTZI (TAG_USER | ( MUISERIALNR_STUNTZI << 16))
  38.  
  39. #define MUIM_Class4_Update (TAGBASE_STUNTZI | 0x0001)
  40. struct MUIP_Class4_Update
  41. {
  42.     ULONG id;
  43.     LONG percent;
  44. };
  45.  
  46. #define MUIM_Class4_Calc   (TAGBASE_STUNTZI | 0x0002)
  47. struct MUIP_Class4_Calc
  48. {
  49.     ULONG id;
  50.     struct FractalDesc *fd;
  51. };
  52.  
  53. #define MUIA_Class4_Percent (TAGBASE_STUNTZI | 0x0003)
  54.  
  55.  
  56. #define STC_START 0
  57. #define STC_STOP  1
  58.  
  59.  
  60. /* Instance Data for the fractal class */
  61.  
  62. struct Data
  63. {
  64.     struct SignalSemaphore sema;    /* data item protection      */
  65.     Object                *app;     /* pointer to application    */
  66.     Object                *self;    /* pointer to ourselves      */
  67.     struct SubTask        *subtask; /* our sub task              */
  68.     struct RastPort        rp;      /* rastport for the sub task */
  69.     BYTE                  *udlines; /* line update flags array   */
  70. };
  71.  
  72.  
  73.  
  74.  
  75. /**************************************************************/
  76. /* Functions for easy and secure spawning/killing of subtasks */
  77. /**************************************************************/
  78.  
  79. struct SubTaskMsg
  80. {
  81.     struct Message stm_Message;
  82.     WORD           stm_Command;
  83.     APTR           stm_Parameter;
  84.     LONG           stm_Result;
  85. };
  86.  
  87. struct SubTask
  88. {
  89.    struct Task      *st_Task;    /* sub task pointer */
  90.     struct MsgPort   *st_Port;    /* allocated by sub task */
  91.     struct MsgPort   *st_Reply;   /* allocated by main task */
  92.     APTR              st_Data;    /* more initial data to pass to the sub task */
  93.     struct SubTaskMsg st_Message; /* Message buffer */
  94. };
  95.  
  96. #define STC_STARTUP  -2
  97. #define STC_SHUTDOWN -1
  98.  
  99. LONG SendSubTaskMsg(struct SubTask *st,WORD command,APTR params)
  100. {
  101.     st->st_Message.stm_Message.mn_ReplyPort = st->st_Reply;
  102.     st->st_Message.stm_Message.mn_Length    = sizeof(struct SubTaskMsg);
  103.     st->st_Message.stm_Command              = command;
  104.     st->st_Message.stm_Parameter            = params;
  105.     st->st_Message.stm_Result               = 0;
  106.  
  107.     PutMsg(command==STC_STARTUP ? &((struct Process *)st->st_Task)->pr_MsgPort : st->st_Port,(struct Message *)&st->st_Message);
  108.     WaitPort(st->st_Reply);
  109.     GetMsg(st->st_Reply);
  110.  
  111.     return(st->st_Message.stm_Result);
  112. }
  113.  
  114. struct SubTask *SpawnSubTask(char *name,VOID (*func)(VOID),APTR data)
  115. {
  116.     struct SubTask *st;
  117.  
  118.     if (st = AllocVec(sizeof(struct SubTask),MEMF_PUBLIC|MEMF_CLEAR))
  119.     {
  120.         if (st->st_Reply = CreateMsgPort())
  121.         {
  122.             st->st_Data = data;
  123.  
  124.             if (st->st_Task = (struct Task *)CreateNewProcTags(NP_Entry,func,NP_Name,name,TAG_DONE))
  125.             {
  126.                 if (SendSubTaskMsg(st,STC_STARTUP,st))
  127.                 {
  128.                     return(st);
  129.                 }
  130.             }
  131.             DeleteMsgPort(st->st_Reply);
  132.         }
  133.         FreeVec(st);
  134.     }
  135.     return(NULL);
  136. }
  137.  
  138. VOID KillSubTask(struct SubTask *st)
  139. {
  140.     SendSubTaskMsg(st,STC_SHUTDOWN,st);
  141.     DeleteMsgPort(st->st_Reply);
  142.     FreeVec(st);
  143. }
  144.  
  145. /* the following functions are called from the sub task */
  146.  
  147. VOID ExitSubTask(struct SubTask *st,struct SubTaskMsg *stm)
  148. {
  149.     /*
  150.     ** We reply after a Forbid() to make sure we're really gone
  151.     ** when the main task continues.
  152.     */
  153.  
  154.     if (st->st_Port)
  155.         DeleteMsgPort(st->st_Port);
  156.  
  157.     Forbid();
  158.     stm->stm_Result = FALSE;
  159.     ReplyMsg((struct Message *)stm);
  160. }
  161.  
  162. struct SubTask *InitSubTask(void)
  163. {
  164.     struct Task *me = FindTask(NULL);
  165.     struct SubTask *st;
  166.     struct SubTaskMsg *stm;
  167.  
  168.     /*
  169.     ** Wait for our startup message from the SpawnSubTask() function.
  170.     */
  171.  
  172.     WaitPort(&((struct Process *)me)->pr_MsgPort);
  173.     stm  = (struct SubTaskMsg *)GetMsg(&((struct Process *)me)->pr_MsgPort);
  174.     st   = (struct SubTask *)stm->stm_Parameter;
  175.  
  176.     if (st->st_Port = CreateMsgPort())
  177.     {
  178.         /*
  179.         ** Reply startup message, everything ok.
  180.         ** Note that if the initialization fails, the code falls
  181.         ** through and replies the startup message with a stm_Result
  182.         ** of 0 after a Forbid(). This tells SpawnSubTask() that the
  183.         ** sub task failed to run.
  184.         */
  185.  
  186.         stm->stm_Result = TRUE;
  187.         ReplyMsg((struct Message *)stm);
  188.         return(st);
  189.     }
  190.     else
  191.     {
  192.         ExitSubTask(st,stm);
  193.         return(NULL);
  194.     }
  195. }
  196.  
  197.  
  198. /*******************************************************/
  199. /* Subtask which does all the time-consuming rendering */
  200. /*******************************************************/
  201.  
  202. VOID __asm __saveds RenderFunc(VOID)
  203. {
  204.     struct SubTask *st;
  205.     struct Library *GfxBase;
  206.  
  207.     if (GfxBase = OpenLibrary("graphics.library",37)) /* dont share library pointers! */
  208.     {
  209.         if (st = InitSubTask())
  210.         {
  211.             struct Data *data = st->st_Data;
  212.             BOOL running = TRUE;
  213.             BOOL worktodo = FALSE;
  214.             LONG x,y;
  215.             struct SubTaskMsg *stm;
  216.  
  217.             /*
  218.             ** after the sub task is up and running, we go into
  219.             ** a loop and process the messages from the main task.
  220.             */
  221.  
  222.             for (;;)
  223.             {
  224.                 float left,top,right,bottom;
  225.  
  226.                 while (stm = (struct SubTaskMsg *)GetMsg(st->st_Port))
  227.                 {
  228.                     switch (stm->stm_Command)
  229.                     {
  230.                         case STC_SHUTDOWN:
  231.                             /*
  232.                             ** This is the shutdown message from KillSubTask().
  233.                             */
  234.                             running = FALSE;
  235.                             break;
  236.  
  237.                         case STC_START:
  238.                             /*
  239.                             ** we received a start message with a fractal description.
  240.                             ** clear the rastport and the line update array and start
  241.                             ** rendering.
  242.                             */
  243.                             SetRast(&data->rp,1);
  244.                             memset(data->udlines,0,FRACTALHEIGHT);
  245.                             left   = ((struct FractalDesc *)stm->stm_Parameter)->left  ;
  246.                             top    = ((struct FractalDesc *)stm->stm_Parameter)->top   ;
  247.                             right  = ((struct FractalDesc *)stm->stm_Parameter)->right ;
  248.                             bottom = ((struct FractalDesc *)stm->stm_Parameter)->bottom;
  249.                             y = 0;
  250.                             worktodo = TRUE;
  251.                             break;
  252.  
  253.                         case STC_STOP:
  254.                             /* this message is not used in this example */
  255.                             worktodo = FALSE;
  256.                             break;
  257.                     }
  258.  
  259.                     /*
  260.                     ** If we received a shutdown message, we do not reply it
  261.                     ** immediately. First, we need to free our resources.
  262.                     */
  263.                     if (!running) break;
  264.  
  265.                     ReplyMsg((struct Message *)stm);
  266.                 }
  267.  
  268.                 if (!running) break;
  269.  
  270.                 if (worktodo)
  271.                 {
  272.                     /* if there is work to do, i.e. if the fractal is not
  273.                     ** finished yet, we calculate the next line and draw
  274.                     ** it to the offscreen rastport.
  275.                    */
  276.  
  277.                     for (x=0;x<FRACTALWIDTH;x++)
  278.                     {
  279.                         float zr = 0.0;
  280.                         float zi = 0.0;
  281.                         float cr = left + x * (right - left) / FRACTALWIDTH;
  282.                         float ci = top  + y * (bottom - top) / FRACTALHEIGHT;
  283.                         float rr = zr * zr;
  284.                         float ii = zi * zi;
  285.                         int counter;
  286.  
  287.                         for (counter=0;counter<MaxIterations;counter++)
  288.                         {
  289.                             zi = ci + zr * zi * 2;
  290.                             zr = cr + rr - ii;
  291.                             rr = zr * zr;
  292.                             ii = zi * zi;
  293.  
  294.                             if (rr + ii >= 4.0)
  295.                             {
  296.                                 /*
  297.                                 ** set the pixel in the offscreen rastport.
  298.                                 ** this demo is kind of dirty, as it does no
  299.                                 ** nice color allocation and palette stuff.
  300.                                 ** dont be so dirty in your own programs! :-)
  301.                                 */
  302.  
  303.                                 SetAPen(&data->rp,1+counter);
  304.                                 WritePixel(&data->rp,x,y);
  305.                                 break;
  306.                             }
  307.  
  308.                             if (++counter==MaxIterations)
  309.                             {
  310.                                 break;
  311.                             }
  312.                         }
  313.                     }
  314.  
  315.                     /*
  316.                     ** after the line is finished, we set the corresponding
  317.                     ** flag in the line update array to FALSE. This shows the
  318.                     ** main task that this line needs to be redrawn the next
  319.                     ** time it gets the chance.
  320.                     */
  321.  
  322.                     ObtainSemaphore(&data->sema);
  323.                     data->udlines[y] = FALSE;
  324.                     if (data->app)
  325.                     {
  326.                         /*
  327.                         ** if our class is attached to an application, we send ourselves
  328.                         ** an update method. Note that because we are in a separate task,
  329.                         ** we cannot send this method directly but instead have to use
  330.                         ** the MUIM_Application_PushMethod call. This is the only method
  331.                         ** that you may send to a MUI object from a separate task. What it
  332.                         ** does is to copy the method to a private buffer and wait until
  333.                         ** the next time the main task calls the input method. Then, our
  334.                         ** update method will be executed under the main tasks context.
  335.                         **
  336.                         ** If our class is not attached to an application
  337.                         ** (i.e. we are outside of MUIM_Setup/MUIM_Cleanup), there is
  338.                         ** nobody who could render something anyway so we just skip
  339.                         ** the update method and continue to render in our private
  340.                         ** buffer.
  341.                         */
  342.  
  343.                         DoMethod(data->app,MUIM_Application_PushMethod,data->self,2,MUIM_Class4_Update,100*(y+1)/FRACTALHEIGHT);
  344.                     }
  345.                     ReleaseSemaphore(&data->sema);
  346.  
  347.                     if (++y==FRACTALHEIGHT)
  348.                     {
  349.                         /* check if we are finished to draw our fractal */
  350.                         worktodo = FALSE;
  351.                     }
  352.  
  353.                     /* Since we are very busy working, we do not Wait() for signals. */
  354.                 }
  355.                 else
  356.                 {
  357.                     /* We have nothing to do, just sit quietly and wait for something to happen */
  358.                     WaitPort(st->st_Port);
  359.                 }
  360.             }
  361.             ExitSubTask(st,stm);
  362.         }
  363.         CloseLibrary(GfxBase);
  364.     }
  365. }
  366.  
  367.  
  368.  
  369. /***************************************************************************/
  370. /* Here is the beginning of our new class...                               */
  371. /***************************************************************************/
  372.  
  373.  
  374. SAVEDS ULONG mNew(struct IClass *cl,Object *obj,Msg msg)
  375. {
  376.     struct Data *data;
  377.  
  378.     if (!(obj = (Object *)DoSuperMethodA(cl,obj,msg)))
  379.         return(0);
  380.  
  381.     data = INST_DATA(cl,obj);
  382.  
  383.     /* store a pointer to ourselves so the subtask knows about us */
  384.     data->self = obj;
  385.  
  386.     /*
  387.     ** initialization and allocation of data structures.
  388.     ** note that if something fails here, we *must* do a
  389.     ** CoerceMethod(cl,obj,OM_DISPOSE) to give ourselves
  390.     ** (and MUI!) a chance to clean up.
  391.     */
  392.  
  393.     InitSemaphore(&data->sema);
  394.     InitRastPort(&data->rp);
  395.  
  396.     if (data->udlines = AllocVec(FRACTALHEIGHT,MEMF_CLEAR))
  397.     {
  398.         if (data->rp.BitMap = AllocBitMap(FRACTALWIDTH,FRACTALHEIGHT,8,BMF_CLEAR,NULL))
  399.         {
  400.             SetRast(&data->rp,1);
  401.  
  402.             /* the following call starts the sub task */
  403.  
  404.             if (data->subtask = SpawnSubTask("Class4-Render-Task",RenderFunc,data))
  405.             {
  406.                 SetTaskPri(data->subtask->st_Task,-1);
  407.                 return((ULONG)obj);
  408.             }
  409.         }
  410.     }
  411.  
  412.     CoerceMethod(cl,obj,OM_DISPOSE);
  413.     return(0);
  414. }
  415.  
  416.  
  417.  
  418. SAVEDS ULONG mDispose(struct IClass *cl,Object *obj,Msg msg)
  419. {
  420.     struct Data *data = INST_DATA(cl,obj);
  421.  
  422.     if (data->subtask)
  423.         KillSubTask(data->subtask);
  424.  
  425.     if (data->rp.BitMap)
  426.         FreeBitMap(data->rp.BitMap);
  427.  
  428.     if (data->udlines)
  429.         FreeVec(data->udlines);
  430.  
  431.     return(DoSuperMethodA(cl,obj,msg));
  432. }
  433.  
  434.  
  435.  
  436. /*
  437. ** AskMinMax method will be called before the window is opened
  438. ** and before layout takes place. We need to tell MUI the
  439. ** minimum, maximum and default size of our object.
  440. */
  441.  
  442. SAVEDS ULONG mAskMinMax(struct IClass *cl,Object *obj,struct MUIP_AskMinMax *msg)
  443. {
  444.     /*
  445.     ** let our superclass first fill in what it thinks about sizes.
  446.     ** this will e.g. add the size of frame and inner spacing.
  447.     */
  448.  
  449.     DoSuperMethodA(cl,obj,msg);
  450.  
  451.     /*
  452.     ** now add the values specific to our object. note that we
  453.     ** indeed need to *add* these values, not just set them!
  454.     */
  455.  
  456.     msg->MinMaxInfo->MinWidth  += 10;
  457.     msg->MinMaxInfo->DefWidth  += 100;
  458.     msg->MinMaxInfo->MaxWidth  += FRACTALWIDTH;
  459.  
  460.     msg->MinMaxInfo->MinHeight += 10;
  461.     msg->MinMaxInfo->DefHeight += 100;
  462.     msg->MinMaxInfo->MaxHeight += FRACTALHEIGHT;
  463.  
  464.     return(0);
  465. }
  466.  
  467.  
  468.  
  469. /*
  470. ** Draw method is called whenever MUI feels we should render
  471. ** our object. This usually happens after layout is finished
  472. ** or when we need to refresh in a simplerefresh window.
  473. ** Note: You may only render within the rectangle
  474. **       _mleft(obj), _mtop(obj), _mwidth(obj), _mheight(obj).
  475. */
  476.  
  477. SAVEDS ULONG mDraw(struct IClass *cl,Object *obj,struct MUIP_Draw *msg)
  478. {
  479.     struct Data *data = INST_DATA(cl,obj);
  480.  
  481.     /*
  482.     ** let our superclass draw itself first, area class would
  483.     ** e.g. draw the frame and clear the whole region. What
  484.     ** it does exactly depends on msg->flags.
  485.     **
  486.     ** Note: You *must* call the super method prior to do
  487.     ** anything else, otherwise msg->flags will not be set
  488.     ** properly!
  489.     */
  490.  
  491.     DoSuperMethodA(cl,obj,msg);
  492.  
  493.     if (msg->flags & MADF_DRAWUPDATE)
  494.     {
  495.         /*
  496.         ** This flag indicates that we were called from our
  497.         ** update method. We needn't render the complete
  498.         ** image, we only need to update the lines that
  499.         ** were changed. So what we do is to browse through
  500.         ** the line flag array and blit each changed line
  501.         ** from the offscreen buffer into the display.
  502.         ** We could do a better and more efficient job
  503.         ** by collecting subsequent changed lines to blit
  504.         ** larger rectangles, but hey... this is only a demo! :-)
  505.         */
  506.  
  507.         int l;
  508.  
  509.         /* 
  510.         ** note the usage of semaphores to protect access
  511.         ** to variables use by both tasks.
  512.         */
  513.  
  514.         ObtainSemaphore(&data->sema);
  515.  
  516.         for (l=0;l<_mheight(obj);l++)
  517.         {
  518.             if (!data->udlines[l])
  519.             {
  520.                 /*
  521.                 ** once we copied the line, we set the corresponding line flag
  522.                 ** to indicate that this line is uptodate and does not need
  523.                 ** to be redrawn the next time. When our sub task gets the message
  524.                 ** to calculate a new fractal, it will reset the flag to FALSE again.
  525.                 */
  526.  
  527.                 BltBitMapRastPort(data->rp.BitMap,0,l,_rp(obj),_mleft(obj),_mtop(obj)+l,_mwidth(obj),1,0xc0);
  528.                 data->udlines[l] = TRUE;
  529.             }
  530.         }
  531.  
  532.         ReleaseSemaphore(&data->sema);
  533.     }
  534.     else if (msg->flags & MADF_DRAWOBJECT)
  535.     {
  536.         /*
  537.         ** we were called directly from MUI because the window needs refresh.
  538.         ** no need to care about our line array here, we just copy the complete
  539.         ** offscreen buffer to our display.
  540.         */
  541.  
  542.         ObtainSemaphore(&data->sema);
  543.         BltBitMapRastPort(data->rp.BitMap,0,0,_rp(obj),_mleft(obj),_mtop(obj),_mwidth(obj),_mheight(obj),0xc0);
  544.         ReleaseSemaphore(&data->sema);
  545.     }
  546.  
  547.     return(0);
  548. }
  549.  
  550.  
  551. SAVEDS ULONG mSetup(struct IClass *cl,Object *obj,struct MUIP_HandleInput *msg)
  552. {
  553.     struct Data *data = INST_DATA(cl,obj);
  554.  
  555.     if (!(DoSuperMethodA(cl,obj,msg)))
  556.         return(FALSE);
  557.  
  558.     /*
  559.     ** set a pointer to our application in our instance data.
  560.     ** this indicates the sub task that we should be notified
  561.     ** when a new line is calculated.
  562.     */
  563.  
  564.     ObtainSemaphore(&data->sema);
  565.     get(obj,MUIA_ApplicationObject,&data->app);
  566.     ReleaseSemaphore(&data->sema);
  567.  
  568.     return(TRUE);
  569. }
  570.  
  571.  
  572. SAVEDS ULONG mCleanup(struct IClass *cl,Object *obj,struct MUIP_HandleInput *msg)
  573. {
  574.     struct Data *data = INST_DATA(cl,obj);
  575.  
  576.     ObtainSemaphore(&data->sema);
  577.     data->app = NULL;
  578.     ReleaseSemaphore(&data->sema);
  579.  
  580.     return(DoSuperMethodA(cl,obj,msg));
  581. }
  582.  
  583.  
  584. /*
  585. ** a simple method that sends a START msg with
  586. ** fractal description packet to the sub task.
  587. */
  588.  
  589. SAVEDS ULONG mCalc(struct IClass *cl,Object *obj,struct MUIP_Class4_Calc *msg)
  590. {
  591.     struct Data *data = INST_DATA(cl,obj);
  592.     SendSubTaskMsg(data->subtask,STC_START,msg->fd);
  593.     return(0);
  594. }
  595.  
  596.  
  597. /*
  598. ** thats the method that is called through MUIM_Application_PushMethod
  599. ** from the subtask.
  600. */
  601.  
  602. SAVEDS ULONG mUpdate(struct IClass *cl,Object *obj,struct MUIP_Class4_Update *msg)
  603. {
  604.     /* Tell MUI to redraw our object. Set the update flag
  605.     ** so we know that only the changed lines are subject
  606.     ** to render.
  607.     */
  608.  
  609.     MUI_Redraw(obj,MADF_DRAWUPDATE);
  610.  
  611.     /*
  612.     ** Also the the percentage attribute. The class itself doesnt
  613.     ** have any use for this, but if we set it, its possible
  614.     ** for other objects (e.g. a gauge) to receive notifications
  615.     */
  616.  
  617.     set(obj,MUIA_Class4_Percent,msg->percent);
  618.  
  619.     return(0);
  620. }
  621.  
  622.  
  623. /*
  624. ** Here comes the dispatcher for our custom class.
  625. ** Unknown/unused methods are passed to the superclass immediately.
  626. */
  627.  
  628. SAVEDS ASM ULONG MyDispatcher(REG(a0) struct IClass *cl,REG(a2) Object *obj,REG(a1) Msg msg)
  629. {
  630.     switch (msg->MethodID)
  631.     {
  632.         case OM_NEW            : return(mNew      (cl,obj,(APTR)msg));
  633.         case OM_DISPOSE        : return(mDispose  (cl,obj,(APTR)msg));
  634.         case MUIM_AskMinMax    : return(mAskMinMax(cl,obj,(APTR)msg));
  635.         case MUIM_Draw         : return(mDraw     (cl,obj,(APTR)msg));
  636.         case MUIM_Setup        : return(mSetup    (cl,obj,(APTR)msg));
  637.         case MUIM_Cleanup      : return(mCleanup  (cl,obj,(APTR)msg));
  638.         case MUIM_Class4_Update: return(mUpdate   (cl,obj,(APTR)msg));
  639.         case MUIM_Class4_Calc  : return(mCalc     (cl,obj,(APTR)msg));
  640.     }
  641.  
  642.     return(DoSuperMethodA(cl,obj,msg));
  643. }
  644.  
  645.  
  646.  
  647. /****************************************************************************/
  648. /* Misc Help Functions                                                      */
  649. /****************************************************************************/
  650.  
  651. LONG xget(Object *obj,ULONG attribute)
  652. {
  653.     LONG x;
  654.     get(obj,attribute,&x);
  655.     return(x);
  656. }
  657.  
  658. char *getstr(Object *obj)
  659. {
  660.     return((char *)xget(obj,MUIA_String_Contents));
  661. }
  662.  
  663.  
  664.  
  665. /***************************************************************************/
  666. /* Thats all there is about it. Now lets see how things are used...        */
  667. /***************************************************************************/
  668.  
  669. int main(int argc,char *argv[])
  670. {
  671.     Object *app,*window,*MyObj;
  672.     Object *strleft,*strtop,*strright,*strbottom,*start,*gauge;
  673.     struct MUI_CustomClass *mcc;
  674.     ULONG signals;
  675.     BOOL running = TRUE;
  676.  
  677.     if (((struct Library *)SysBase)->lib_Version < 39)
  678.     {
  679.         fprintf(stderr,"runs only with V39 and up\n");
  680.         exit(20);
  681.     }
  682.  
  683.     init();
  684.  
  685.     /* Create the new custom class with a call to MUI_CreateCustomClass(). */
  686.     /* Caution: This function returns not a struct IClass, but a           */
  687.     /* struct MUI_CustomClass which contains a struct IClass to be         */
  688.     /* used with NewObject() calls.                                        */
  689.     /* Note well: MUI creates the dispatcher hook for you, you may         */
  690.     /* *not* use its h_Data field! If you need custom data, use the        */
  691.     /* cl_UserData of the IClass structure!                                */
  692.  
  693.     if (!(mcc = MUI_CreateCustomClass(NULL,MUIC_Area,NULL,sizeof(struct Data),MyDispatcher)))
  694.         fail(NULL,"Could not create custom class.");
  695.  
  696.     app = ApplicationObject,
  697.         MUIA_Application_Title      , "Class4",
  698.         MUIA_Application_Version    , "$VER: Class4 19.5 (12.02.97)",
  699.         MUIA_Application_Copyright  , "©1993, Stefan Stuntz",
  700.         MUIA_Application_Author     , "Stefan Stuntz",
  701.         MUIA_Application_Description, "Demonstrate rendering from sub tasks.",
  702.         MUIA_Application_Base       , "Class4",
  703.  
  704.         SubWindow, window = WindowObject,
  705.             MUIA_Window_Title, "Subtask rendering",
  706.             MUIA_Window_ID   , MAKE_ID('C','L','S','4'),
  707.             WindowContents, VGroup,
  708.  
  709.                 Child, HGroup, GroupSpacing(8),
  710.                     Child, ColGroup(2),
  711.                         Child, Label2("_Left:"  ), Child, strleft   = MUI_MakeObject(MUIO_String,"_L",30),
  712.                         Child, Label2("_Right:" ), Child, strright  = MUI_MakeObject(MUIO_String,"_R",30),
  713.                         End,
  714.                     Child, ColGroup(2),
  715.                         Child, Label2("_Top:"   ), Child, strtop    = MUI_MakeObject(MUIO_String,"_T",30),
  716.                         Child, Label2("_Bottom:"), Child, strbottom = MUI_MakeObject(MUIO_String,"_B",30),
  717.                         End,
  718.                     Child, MUI_MakeObject(MUIO_VBar,2),
  719.                     Child, start = VGroup,
  720.                         GroupSpacing(0),
  721.                         MUIA_Weight, 0,
  722.                         ButtonFrame,
  723.                         MUIA_InputMode , MUIV_InputMode_RelVerify,
  724.                         MUIA_Background, MUII_ButtonBack,
  725.                         Child, VSpace(0),
  726.                         Child, TextObject, MUIA_Text_Contents, "\33c  Start  ", End,
  727.                         Child, VSpace(0),
  728.                         End,
  729.                     End,
  730.  
  731.                 Child, gauge = GaugeObject,
  732.                     GaugeFrame,
  733.                     MUIA_Gauge_Horiz, TRUE,
  734.                     MUIA_Gauge_Max, 100,
  735.                     MUIA_FixHeight, 8,
  736.                     End,
  737.  
  738.                 Child, MyObj = NewObject(mcc->mcc_Class,NULL,
  739.                     TextFrame,
  740.                     MUIA_Background, MUII_BACKGROUND,
  741.                     TAG_DONE),
  742.  
  743.                 End,
  744.             End,
  745.         End;
  746.  
  747.     if (!app)
  748.         fail(app,"Failed to create Application.");
  749.  
  750.     set(window,MUIA_Window_DefaultObject, MyObj);
  751.  
  752.     DoMethod(window,MUIM_Notify,MUIA_Window_CloseRequest,TRUE,
  753.         app,2,MUIM_Application_ReturnID,MUIV_Application_ReturnID_Quit);
  754.  
  755.     DoMethod(start,MUIM_Notify,MUIA_Pressed,FALSE,
  756.         app,2,MUIM_Application_ReturnID,1);
  757.  
  758.     DoMethod(MyObj,MUIM_Notify,MUIA_Class4_Percent,MUIV_EveryTime,
  759.         gauge,3,MUIM_Set,MUIA_Gauge_Current,MUIV_TriggerValue);
  760.  
  761.     set(strleft  ,MUIA_String_Contents,"-2.0");
  762.     set(strright ,MUIA_String_Contents,"1.0");
  763.     set(strtop   ,MUIA_String_Contents,"1.5");
  764.     set(strbottom,MUIA_String_Contents,"-1.5");
  765.  
  766. /*
  767. ** Input loop...
  768. */
  769.  
  770.     set(window,MUIA_Window_Open,TRUE);
  771.  
  772.     while (running)
  773.     {
  774.         switch (DoMethod(app,MUIM_Application_Input,&signals))
  775.         {
  776.             case MUIV_Application_ReturnID_Quit:
  777.                 running = FALSE;
  778.                 break;
  779.  
  780.             case 1:
  781.             {
  782.                 struct FractalDesc fd;
  783.  
  784.                 fd.left   = atof(getstr(strleft  ));
  785.                 fd.right  = atof(getstr(strright ));
  786.                 fd.top    = atof(getstr(strtop   ));
  787.                 fd.bottom = atof(getstr(strbottom));
  788.  
  789.                 if (fd.right > fd.left && fd.top > fd.bottom)
  790.                     DoMethod(MyObj,MUIM_Class4_Calc,&fd);
  791.                 else
  792.                     DisplayBeep(0);
  793.             }
  794.             break;
  795.         }
  796.  
  797.         if (running && signals) Wait(signals);
  798.     }
  799.  
  800.     set(window,MUIA_Window_Open,FALSE);
  801.  
  802.  
  803. /*
  804. ** Shut down...
  805. */
  806.  
  807.     MUI_DisposeObject(app);     /* dispose all objects. */
  808.     MUI_DeleteCustomClass(mcc); /* delete the custom class. */
  809.     fail(NULL,NULL);            /* exit, app is already disposed. */
  810. }
  811.